package cn.exrick.xboot.modules.transport.service.impl;

import cn.exrick.xboot.common.constant.CommonConstant;
import cn.exrick.xboot.common.exception.XbootException;
import cn.exrick.xboot.common.utils.BeanConverterUtil;
import cn.exrick.xboot.common.utils.DateUtils;
import cn.exrick.xboot.common.utils.SecurityUtil;
import cn.exrick.xboot.modules.base.dao.DictDataDao;
import cn.exrick.xboot.modules.base.dao.mapper.UserMapper;
import cn.exrick.xboot.modules.base.entity.DictData;
import cn.exrick.xboot.modules.base.entity.User;
import cn.exrick.xboot.modules.base.enums.*;
import cn.exrick.xboot.modules.transport.baoxian.config.InsuranceStatus;
import cn.exrick.xboot.modules.transport.baoxian.dto.InsuranceApplicationRequest;
import cn.exrick.xboot.modules.transport.baoxian.dto.InsuranceApplyResponse;
import cn.exrick.xboot.modules.transport.baoxian.service.InsuranceService;
import cn.exrick.xboot.modules.transport.entity.*;
import cn.exrick.xboot.modules.transport.gps.dto.GpsFindDevRequest;
import cn.exrick.xboot.modules.transport.gps.dto.GpsFindDevResponse;
import cn.exrick.xboot.modules.transport.gps.dto.GpsQueryLocationRequest;
import cn.exrick.xboot.modules.transport.gps.dto.GpsQueryLocationResponse;
import cn.exrick.xboot.modules.transport.gps.service.GpsApiService;
import cn.exrick.xboot.modules.transport.mapper.*;
import cn.exrick.xboot.modules.transport.param.*;
import cn.exrick.xboot.modules.transport.service.DealRecordService;
import cn.exrick.xboot.modules.transport.service.OrdersService;
import cn.exrick.xboot.modules.transport.vo.*;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static cn.exrick.xboot.common.utils.DateUtils.DEFAULT_DATE_H_FORMAT;

/**
 * 订单表接口实现
 *  //根据订单模式判断订单状态，选择选择货到付款|大客户模式 ->已下单
 *         //选择直接支付 ->待支付
 *         //用户待支付订单点击取消支付 -> 已取消
 *         //物流公司点接单 -> 已接单
 *         //实时gps与起运仓距离超过50公里 -> 运输中
 *         //中转公司点击中转接单 -> 转运中
 *         //目的仓公司点到达接单 -> 已达到
 *         //用户点击已收货->已收货
 *         //已收货后先付款|大客户模式 -> 已完成
 *         //已收货后货到付款 下单人订单上支付或者接车人扫码支付发单人提供二维码 -> 已完成
 */
@Slf4j
@Service
@Transactional
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements OrdersService {

    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private SecurityUtil securityUtil;
    @Autowired
    private OrderCarInfoMapper orderCarInfoMapper;
    @Autowired
    private OrderCarFileMapper orderCarFileMapper;
    @Autowired
    private CompanyRoutesMapper companyRoutesMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private CompanyWarehousesMapper companyWarehousesMapper;
    @Autowired
    private OrderCarNodeInfoMapper orderCarNodeInfoMapper;
    @Autowired
    private DealRecordService dealRecordService;
    @Autowired
    private DictDataDao dictDataDao;
    @Autowired
    private CompanyDepositDetailsMapper companyDepositDetailsMapper;
    @Autowired
    private InsuranceService insuranceService;
    @Autowired
    private GpsApiService gpsApiService;
    @Autowired
    private RegionsMapper regionsMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrdersVo userCreatesOrder(CreateOrdersParam param) {
        Orders order = BeanConverterUtil.convertBean(param, Orders.class);
        User currUser = securityUtil.getCurrUser();
        //根据订单模式判断订单状态，选择选择货到付款|大客户模式 ->已下单 选择直接支付 ->待支付
        //获取用户角色列表，判断是否为大客户
        if (StrUtil.isBlank(order.getMode())) {
            throw new XbootException("请选择订单模式");
        }else {
            if (order.getMode().equals(OrderModeEnum.BIG_CUSTOMER.getValue())) {
                if(Objects.isNull(currUser.getIsBigCustomers())){
                    throw new XbootException("您不是大客户");
                }
                order.setCustomerId(currUser.getIsBigCustomers().getCustomerId());
                order.setStatus(OrderStatusEnum.Placed.getValue());
                order.setPaymentStatus(PaymentStatusEnum.NO_PAYMENT_NEEDED.getValue());
            } else if (order.getMode().equals(OrderModeEnum.MULTI_UNIT.getValue())) {
                if (order.getQuantity() < 3){
                    throw new XbootException("多台订单，车辆数量应不小于3台");
                }
                order.setPaymentStatus(PaymentStatusEnum.UNPAID.getValue());
            }else{
                order.setPaymentStatus(PaymentStatusEnum.UNPAID.getValue());
            }
            if (StrUtil.isBlank(order.getStatus())) {
                if (StrUtil.isBlank(order.getPaymentMethod())) {
                    throw new XbootException("请选择支付方式");
                }
                if (order.getPaymentMethod().equals(PaymentMethodEnum.PayNow.getValue())) {
                    order.setStatus(OrderStatusEnum.PendingPayment.getValue());
                } else if (order.getPaymentMethod().equals(PaymentMethodEnum.PayOnDelivery.getValue())) {
                    order.setStatus(OrderStatusEnum.Placed.getValue());
                }else {
                    throw new XbootException("请选择支付方式");
                }
            }
        }
        //
        order.setSenderId(currUser.getId());
        //添加个订单的编号
        order.setOrderNumber("XBT" + System.currentTimeMillis());
        ordersMapper.insert(order);
        param.getOrderCarInfoParamList().forEach(orderCarInfoParam -> {
            OrderCarInfo orderCarInfo = BeanConverterUtil.convertBean(orderCarInfoParam, OrderCarInfo.class);
            orderCarInfo.setOrderId(order.getId());
            orderCarInfoMapper.insert(orderCarInfo);
        });
        DictData byTitle = dictDataDao.findByTitle(DictTypeEnum.perOrderDeduction.getValue());
        order.setPerOrderDeduction(BigDecimal.valueOf(Long.parseLong(byTitle.getValue())));
        order.setActualPayment(order.getOrderAmount().min(order.getDiscountAmount()));
        ordersMapper.updateById(order);
        return this.getOrderById(order.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrdersVo setOrderReceiverId(OrderReceiverParam param) {
        //获取订单信息、收货人用户信息，如果参数中传入名称和电话号码，根据传入的名称和电话号码查询用户信息，更新订单中的收货人信息，如果未传入名称和电话号则使用收货人的电话和昵称，如果收货人user信息不存在则抛异常
        User receiver = userMapper.selectById(param.getReceiverId());
        if (Objects.isNull(receiver)) {
            throw new XbootException("收货人信息不存在");
        }
        Orders order = ordersMapper.selectById(param.getOrderId());
        order.setReceiverId(receiver.getId());
        order.setReceiverName(StrUtil.isBlank(param.getReceiverName()) ? receiver.getNickname() : param.getReceiverName());
        order.setReceiverPhone(StrUtil.isBlank(param.getReceiverPhone()) ? receiver.getMobile() : param.getReceiverPhone());
        ordersMapper.updateById(order);
        return this.getOrderById(order.getId());
    }

    @Override
    public OrdersVo getOrderById(Long id) {
        OrdersVo orderById = ordersMapper.getOrderById(id);
        List<OrderCarInfo> orderCarInfos = orderCarInfoMapper.selectList(new LambdaQueryWrapper<OrderCarInfo>().eq(OrderCarInfo::getOrderId, id));
        if (CollectionUtil.isNotEmpty(orderCarInfos)) {
            List<OrderCarInfoVo> orderCarInfoVos = BeanConverterUtil.convertList(orderCarInfos, OrderCarInfoVo.class);
            orderCarInfoVos.forEach(orderCarInfo -> {
                List<OrderCarFile> orderCarFiles = orderCarFileMapper.selectList(new LambdaQueryWrapper<OrderCarFile>().eq(OrderCarFile::getOrderCarId, orderCarInfo.getId()));
                orderCarInfo.setFileUrlList(orderCarFiles);
            });
            orderById.setOrderCarInfoVoList(orderCarInfoVos);
        }
        orderById.setCompanyRoutesVo(companyRoutesMapper.getRoutesById(orderById.getRouteId()));
        return orderById;
    }

    @Override
    public IPage<OrdersOutlineVo> getByCondition(Page page, QueryOrdersParam param) {
        return ordersMapper.getByCondition(page,param);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrdersVo userCancelOrder(CancelOrdersParam cancelOrdersParam) {
        //获取订单信息
        Orders order = ordersMapper.selectById(cancelOrdersParam.getOrderId());
        //获取当前用户
        User currUser = securityUtil.getCurrUser();
        //操作人必须和下单人一致，或者为平台管理员，否则抛异常
        if (!currUser.getId().equals(order.getSenderId()) && !currUser.getIsAdmin()) {
            throw new XbootException("您无权取消该订单");
        }
        //如果为下单人取消，只能在待支付状态下取消
        if (currUser.getId().equals(order.getSenderId()) && !currUser.getIsAdmin()) {
            //已收货 已完成 已取消的订单不能取消
            if (!order.getStatus().equals(OrderStatusEnum.PendingPayment.getValue())) {
                throw new XbootException("非待支付状态下无法取消订单");
            }
        }
        //查看用户是否已支付，如已支付需要退款
        if (order.getPaymentStatus().equals(PaymentStatusEnum.UNPAID.getValue())) {
            //已拉起支付的 清空支付信息
            if(StrUtil.isNotEmpty(order.getPayInfo())){
                //拉起支付了，但没完成支付
                dealRecordService.cancelDealRecord(Long.parseLong(order.getPayInfo()));
            }
        }else if (order.getPaymentStatus().equals(PaymentStatusEnum.PAID.getValue())) {
            //查询已支付的记录，如有，则退款
            DealRecord chargeRecord = dealRecordService.getById(Long.parseLong(order.getPayInfo()));
            SaveDealRecordParam dealRecordParam = SaveDealRecordParam.builder()
                    .dealType(DealTypeEnum.PAY.getCode())
                    .dealAmount(chargeRecord.getDealAmount())
                    .orderId(order.getId())
                    .paymentType(PaymentTypeEnum.WECHAT.getCode())
                    .wxTradeType(TradeTypeEnum.JSAPI)
                    .wxAppTypeCode(WxAppTypeEnum.MA.getCode())
                    .dealDescription(CommonConstant.DEAL_DESCRIPTION_REGISTRATION)
                    .relevanceId(chargeRecord.getId())
                    .orderType(DealOrderTypeEnum.ORDER.getCode()).build();
            //未抛异常证明流程正常
            DealRecordVo dealRecordVo = dealRecordService.saveDealRecord(dealRecordParam);
            order.setReturnPayInfo(dealRecordVo.getId().toString());
        }
        //订单状态 不是 PendingPayment  Placed Cancelled 这三个状态的时候 要退还仓点的抵押金
        if (!Arrays.asList(OrderStatusEnum.Placed.getValue(), OrderStatusEnum.PendingPayment.getValue(), OrderStatusEnum.Cancelled.getValue()).contains(order.getStatus())) {
            //扣除押金,生成仓点押金明细
            CompanyRoutes companyRoutes = companyRoutesMapper.selectById(order.getRouteId());
            CompanyWarehouses companyWarehouses = companyWarehousesMapper.selectById(companyRoutes.getOriginWarehouseId());
            CompanyWarehouses updateCompanyWarehouses = new CompanyWarehouses();
            updateCompanyWarehouses.setId(companyWarehouses.getId());
            updateCompanyWarehouses.setRemainingDeposit(companyWarehouses.getRemainingDeposit().add(order.getPerOrderDeduction()));
            companyWarehousesMapper.updateById(updateCompanyWarehouses);
            CompanyDepositDetails companyDepositDetails = new CompanyDepositDetails();
            companyDepositDetails.setCompanyId(companyWarehouses.getCompanyId());
            companyDepositDetails.setWarehouseId(companyWarehouses.getId());
            companyDepositDetails.setDepositType(DepositTypeEnum.REFUND.getValue());
            companyDepositDetails.setAmount(order.getPerOrderDeduction());
            companyDepositDetails.setOrderId(order.getId());
            companyDepositDetailsMapper.insert(companyDepositDetails);
        }

        //如果为管理员取消，在取消原因中加入"管理员取消-",并且不限制订单状态
        if (currUser.getIsAdmin()) {
            cancelOrdersParam.setCancellationReason("管理员取消-" + cancelOrdersParam.getCancellationReason());
        }
        //取消订单，将订单状态更新为 cancelled ，并更新 取消原因
        order.setStatus(OrderStatusEnum.Cancelled.getValue());
        order.setCancellationReason(cancelOrdersParam.getCancellationReason());
        ordersMapper.updateById(order);
        return this.getOrderById(order.getId());
    }

    /**
     * 仓点接单
     * @param receiveOrdersParam
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrdersVo warehousesReceiveOrder(ReceiveOrdersParam receiveOrdersParam) {
        //校验订单状态，非placed 状态抛异常
        Orders order = ordersMapper.selectById(receiveOrdersParam.getOrderId());
        if (!order.getStatus().equals(OrderStatusEnum.Placed.getValue())) {
            throw new XbootException("订单状态异常");
        }
        //判断当前操作人生效的角色是否是仓点管理员，并且对应的仓点是否是订单线路中的物流公司，否的话抛异常
        User currUser = securityUtil.getCurrUser();
        if(currUser.getCurrentRole() == null || !currUser.getCurrentRole().getRoleName().endsWith(RoleEnum.WAREHOUSE_ADMIN.getValue())){
            throw new XbootException("当前角色不可进行接单操作");
        }
        CompanyRoutes companyRoutes = companyRoutesMapper.selectById(order.getRouteId());
        CompanyWarehouses companyWarehouses = companyWarehousesMapper.selectById(currUser.getCurrentRole().getWarehouseId());
        if (!companyRoutes.getCompanyId().equals(companyWarehouses.getCompanyId())){
            throw new XbootException("当前角色不可进行接单操作");
        }
        //校验中转点最后和目的地地区是否相同,获取最后个id
        String lastNode = Arrays.stream(receiveOrdersParam.getNodeMap().split(","))
                .reduce((first, second) -> second)
                .orElseThrow(() -> new XbootException("目的地仓点id不能为空"));
        if (!order.getDestinationRegionId().equals(companyWarehousesMapper.selectById(Long.parseLong(lastNode)).getRegionId())){
            throw new XbootException("目的地仓点id,与订单中目的地地区不一致");
        }

        //通过字典设置中perOrderDeduction 获取每单扣除押金金额，判断当前仓点押金是否充足，不充足提示充值
        BigDecimal perOrderDeduction = order.getPerOrderDeduction();
        BigDecimal orderCarDeposit = companyWarehouses.getRemainingDeposit();
        if (orderCarDeposit.compareTo(perOrderDeduction) < 0){
            throw new XbootException("您的押金不足，请先充值");
        }
        //扣除押金,生成仓点押金明细
        CompanyWarehouses updateCompanyWarehouses = new CompanyWarehouses();
        updateCompanyWarehouses.setId(companyWarehouses.getId());
        updateCompanyWarehouses.setRemainingDeposit(orderCarDeposit.subtract(perOrderDeduction));
        companyWarehousesMapper.updateById(updateCompanyWarehouses);
        CompanyDepositDetails companyDepositDetails = new CompanyDepositDetails();
        companyDepositDetails.setCompanyId(companyWarehouses.getCompanyId());
        companyDepositDetails.setWarehouseId(companyWarehouses.getId());
        companyDepositDetails.setDepositType(DepositTypeEnum.DEDUCTION.getValue());
        companyDepositDetails.setAmount(perOrderDeduction);
        companyDepositDetails.setOrderId(order.getId());
        companyDepositDetailsMapper.insert(companyDepositDetails);

        //将订单中线路的起点仓点id添加到nodeMap中并更新到order中
        order.setNodeMap(companyRoutes.getOriginWarehouseId() + "," + receiveOrdersParam.getNodeMap());
        //设置order 的当前nodeId 和下一个nodeId
        order.setCurrentNodeId(companyRoutes.getOriginWarehouseId());
        order.setNextNodeId(Long.parseLong(Arrays.stream(receiveOrdersParam.getNodeMap().split(",")).findFirst().orElseThrow(() -> new XbootException("目的地仓点id不能为空"))));
        //更新订单状态为已接单
        order.setStatus(OrderStatusEnum.Accepted.getValue());
        order.setAcceptanceTime(LocalDateTime.now());
        ordersMapper.updateById(order);
        //更新订单车辆信息
        Regions originRegion = regionsMapper.selectById(companyRoutes.getOriginRegionId());
        Regions destinationRegion = regionsMapper.selectById(companyRoutes.getDestinationRegionId());

        receiveOrdersParam.getOrderCarInfoParamList().forEach(carInfo->{
            OrderCarInfo orderCarInfo = orderCarInfoMapper.selectById(carInfo.getOrderCarInfoId());
            orderCarInfo.setCarNumber(carInfo.getCarNumber());
            // todo 对接保险 下保险单
            orderCarInfo.setPlateNumber(carInfo.getPlateNumber());
            InsuranceApplicationRequest insuranceApplicationRequest = new InsuranceApplicationRequest();
            insuranceApplicationRequest.getPolicy().setTransport(carInfo.getPlateNumber());
            insuranceApplicationRequest.getPolicy().setPrimaryNo(carInfo.getOrderCarInfoId().toString());
            insuranceApplicationRequest.getPolicy().setFromLoc(originRegion.getRegionsCode());
            insuranceApplicationRequest.getPolicy().setToLoc(destinationRegion.getRegionsCode());
            insuranceApplicationRequest.getPolicy().setDepartureDate(DateUtils.format(LocalDateTime.now(),DEFAULT_DATE_H_FORMAT));
            insuranceApplicationRequest.getPolicy().setEffDate(DateUtils.format(order.getCreateTime(),DEFAULT_DATE_H_FORMAT));
            insuranceApplicationRequest.getPolicy().setInsuredAmount(orderCarInfo.getInsuranceAmount());
            InsuranceApplyResponse insuranceApplyResponse = insuranceService.submitApplication(insuranceApplicationRequest);
            if (ObjectUtil.isNotNull(insuranceApplyResponse)){
                orderCarInfo.setInsurancePolicyNo(insuranceApplyResponse.getPolicy().getPolicyNo());
                orderCarInfo.setInsuranceStatus(InsuranceStatus.TO_BE_REVIEWED.getDesc());
                orderCarInfo.setInsuranceRatio(insuranceApplicationRequest.getPolicy().getRatio());
                orderCarInfo.setInsuranceFee(orderCarInfo.getInsuranceAmount().multiply(insuranceApplicationRequest.getPolicy().getRatio()));
                orderCarInfo.setInsuranceCreateTime(LocalDateTime.now());
                orderCarInfo.setInsuranceUpdateTime(LocalDateTime.now());
            }
            // todo 对接GPS
            orderCarInfo.setGpsImei(carInfo.getGpsImei());
            orderCarInfoMapper.updateById(orderCarInfo);
            //更新车辆图片
            carInfo.getFileUrlList().forEach(file->{
                orderCarFileMapper.insert(new OrderCarFile().setOrderCarId(carInfo.getOrderCarInfoId()).setFileUrl(file));
            });
            //新增车辆起点节点信息
            orderCarNodeInfoMapper.insert(new OrderCarNodeInfo()
                    .setOrderId(order.getId())
                    .setOrderCarId(carInfo.getOrderCarInfoId())
                    .setWarehouseId(companyRoutes.getOriginWarehouseId())
                    .setWarehouseName(companyWarehousesMapper.selectById(companyRoutes.getOriginWarehouseId()).getWarehouseName())
                    .setOperatorId(currUser.getId())
                    .setOperatorName(currUser.getNickname())
            );
        });
        return ordersMapper.getOrderById(order.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderCarNodeResultVo warehousesWayOrder(WayOrderParam param) {
        //获取订单当前节点信息
        OrderCarInfo orderCarInfo = orderCarInfoMapper.selectById(param.getOrderCarInfoId());
        if (orderCarInfo == null){
            throw new XbootException("订单车辆信息不存在");
        }
        Orders order = ordersMapper.selectById(orderCarInfo.getOrderId());
        //获取当前用户是否为 订单下一个节点的仓点管理员，不是抛异常
        User currUser = securityUtil.getCurrUser();
        if(currUser.getCurrentRole() == null || !currUser.getCurrentRole().getRoleName().endsWith(RoleEnum.WAREHOUSE_ADMIN.getValue())
                    || !order.getNextNodeId().equals(currUser.getCurrentRole().getWarehouseId())){
            throw new XbootException("当前角色不可进行确认操作");
        }


        //获取下一个节点订单车辆的节点确认信息
        List<OrderCarNodeInfo> orderCarNodeInfos = orderCarNodeInfoMapper.selectList(new LambdaQueryWrapper<OrderCarNodeInfo>()
                .eq(OrderCarNodeInfo::getOrderId, order.getId())
                .eq(OrderCarNodeInfo::getOrderCarId, param.getOrderCarInfoId())
                .eq(OrderCarNodeInfo::getWarehouseId, order.getNextNodeId()));
        //获取订单车辆信息
        List<OrderCarInfo> orderCarInfos = orderCarInfoMapper.selectList(new LambdaQueryWrapper<OrderCarInfo>().eq(OrderCarInfo::getOrderId, order.getId()));
        //判断当前节点的车辆确认信息是否存在，不存在新增数据
        if (CollectionUtil.isEmpty(orderCarNodeInfos) || orderCarNodeInfos.stream().filter(orderCarNodeInfo ->
                orderCarNodeInfo.getWarehouseId().equals(order.getNextNodeId()) && orderCarNodeInfo.getOrderCarId().equals(param.getOrderCarInfoId())).count() == 0){
            orderCarNodeInfoMapper.insert(new OrderCarNodeInfo()
                    .setOrderId(order.getId())
                    .setOrderCarId(orderCarInfo.getId())
                    .setWarehouseId(order.getNextNodeId())
                    .setWarehouseName(companyWarehousesMapper.selectById(order.getNextNodeId()).getWarehouseName())
                    .setOperatorId(currUser.getId())
                    .setOperatorName(currUser.getNickname())
            );
            //重新获取确认信息
            orderCarNodeInfos = orderCarNodeInfoMapper.selectList(new LambdaQueryWrapper<OrderCarNodeInfo>()
                    .eq(OrderCarNodeInfo::getOrderId, order.getId())
                    .eq(OrderCarNodeInfo::getOrderCarId, param.getOrderCarInfoId())
                    .eq(OrderCarNodeInfo::getWarehouseId, order.getNextNodeId()));
        }
        //通过订单车辆信息判断当前节点的车辆确认信息，是否都已确认，判断依据 如果orderCarId 在 orderCarNodeInfos 对应节点中存在数据说明是已确认，统计出确认数量和未确认数量，并在记录中标注确认状态
        OrderCarNodeResultVo resultVo = new OrderCarNodeResultVo();
        resultVo.setOrderId(order.getId())
                .setOrderCarId(param.getOrderCarInfoId())
                .setCurrentNode(order.getNextNodeId());

        // 计算已完成数和待扫码数
        List<OrderCarNodeInfo> finalOrderCarNodeInfos = orderCarNodeInfos;
        int completedCount = (int) orderCarInfos.stream()
                .filter(carInfo -> finalOrderCarNodeInfos.stream()
                        .anyMatch(nodeInfo -> nodeInfo.getOrderCarId().equals(carInfo.getId())))
                .count();
        int pendingCount = orderCarInfos.size() - completedCount;
        // 设置已完成数和待扫码数
        resultVo.setCurrentNodeCompletedNumber(completedCount)
                .setCurrentNodePendingNumber(pendingCount);
        // 设置订单状态：
        resultVo.setCurrentNodeStatus(completedCount == orderCarInfos.size());
        // 构建车辆扫描详情列表
        List<OrderCarNodeInfoVo> scanDetailList = orderCarInfos.stream()
                .map(carInfo -> {
                    OrderCarNodeInfoVo orderCarNodeInfoVo = BeanConverterUtil.convertBean(carInfo, OrderCarNodeInfoVo.class);
                    orderCarNodeInfoVo.setOrderCarId(carInfo.getId());
                    boolean isConfirmed = finalOrderCarNodeInfos.stream()
                            .anyMatch(nodeInfo -> nodeInfo.getOrderCarId().equals(carInfo.getId()));
                    orderCarNodeInfoVo.setIsConfirm(isConfirmed ? 1 : 0);
                    return orderCarNodeInfoVo;
                })
                .collect(Collectors.toList());
        resultVo.setCurrentNodeCarScanDetailList(scanDetailList);
        //判断当前节点是否全部完成，完成的话将订单中当前节点和下一节点，根据nodeMap中的节点顺序更新，如果下一节点为最后节点则将订单状态更新为已到达
        if (resultVo.getCurrentNodeStatus()){
            //在 nodeMap中 获取 order.getNextNodeId() ,和在下一个节点id
            String[] nodeIds = order.getNodeMap().split(",");
            int currentIndex = Arrays.asList(nodeIds).indexOf(order.getNextNodeId().toString());
            if (currentIndex != -1 && currentIndex < nodeIds.length - 1) {
                Long newNextNodeId = Long.parseLong(nodeIds[currentIndex + 1]);
                order.setNextNodeId(newNextNodeId);
                order.setCurrentNodeId(order.getNextNodeId());
                order.setStatus(OrderStatusEnum.Transferring.getValue());
            }else {
                //如果是大客户订单 订单状态为已收货，否则为已到达状态
                if(OrderModeEnum.BIG_CUSTOMER.getValue().equals(order.getMode())){
                    order.setStatus(OrderStatusEnum.Received.getValue());
                }else {
                    order.setStatus(OrderStatusEnum.Arrived.getValue());
                }
                order.setArrivalTime(LocalDateTime.now());
                order.setSpeedLevel(calculateSpeedCategory(order).getValue());

                //获取GPS轨迹信息 进行存储落库
                orderCarInfos.forEach(carInfo -> {
                    GpsFindDevRequest gpsFindDevRequest = new GpsFindDevRequest();
                    gpsFindDevRequest.getParams().setImei(orderCarInfo.getGpsImei());
                    List<GpsFindDevResponse> devices = gpsApiService.findDevices(gpsFindDevRequest);
                    if (CollectionUtil.isNotEmpty(devices)) {
                        GpsQueryLocationRequest gpsQueryLocationRequest = new GpsQueryLocationRequest();
                        gpsQueryLocationRequest.getParams().setSimei(devices.get(devices.size() - 1).getSimei());
                        ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
                        gpsQueryLocationRequest.getParams().setTime_begin(order.getAcceptanceTime().atZone(shanghaiZone).toEpochSecond());
                        gpsQueryLocationRequest.getParams().setTime_end(LocalDateTime.now().atZone(shanghaiZone).toEpochSecond());
                        List<GpsQueryLocationResponse> gpsQueryLocationResponses = gpsApiService.queryLocation(gpsQueryLocationRequest);
                        if (CollectionUtil.isNotEmpty(gpsQueryLocationResponses)) {
                            carInfo.setGpsLocation(
                                    gpsQueryLocationResponses.stream().map(l -> l.getLat() + "," + l.getLon()).collect(Collectors.joining("|"))
                            );
                            orderCarInfoMapper.updateById(carInfo);
                        }
                    }
                });
            }
            ordersMapper.updateById(order);
        }
        return resultVo;
    }

    private SpeedCategoryEnum calculateSpeedCategory(Orders order) {
        if (OrderStatusEnum.Arrived.getValue().equals(order.getStatus())) {
            LocalDateTime startTime = order.getDeliveryTime();
            if (startTime == null) {
                startTime = order.getAcceptanceTime();
            }
            LocalDateTime arriveTime = order.getArrivalTime();

            // Calculate hours between create and arrive time
            long hours = ChronoUnit.HOURS.between(startTime, arriveTime);
            if (hours == 0) hours = 1; // Avoid division by zero

            // Calculate speed (km per 24 hours)
            double speed = (order.getKilometers() * 24.0) / hours;

            if (speed >= 400) {
                return SpeedCategoryEnum.FAST;
            } else if (speed >= 300) {
                return SpeedCategoryEnum.MEDIUM;
            } else {
                return SpeedCategoryEnum.SLOW;
            }
        }
        return null;
    }

    @Override
    public OrdersVo userConfirmOrder(Long orderId) {
        //操作用户必须是 订单中的下单人或收货人 之一
        // 获取订单
        Orders order = ordersMapper.selectById(orderId);
        if (order == null) {
            throw new XbootException("该订单不存在");
        }
        // 获取当前操作人
        User currUser = securityUtil.getCurrUser();
        if (!order.getSenderId().equals(currUser.getId()) &&!order.getReceiverId().equals(currUser.getId())) {
            throw new XbootException("您无权确认此订单");
        }
        // 确认人
        order.setReceiverUser(currUser.getId());
        order.setReceiptTime(LocalDateTime.now());
       //
        if (order.getPaymentStatus().equals(PaymentStatusEnum.UNPAID.getValue())) {
            throw new XbootException("订单未支付，请支付后确认收货");
        } else {
            order.setStatus(OrderStatusEnum.Received.getValue());
        }
        ordersMapper.updateById(order);
        return ordersMapper.getOrderById(order.getId());
    }

    @Override
    public OrdersVo paymentOrder(Long orderId) {
        OrdersVo ordersVo = new OrdersVo();
        Orders order = ordersMapper.selectById(orderId);
        //判断订单支付状态，非未支付状态 抛异常
        if (!StrUtil.equals(order.getPaymentStatus(), PaymentStatusEnum.UNPAID.getValue())) {
            throw new XbootException("该订单无需支付");
        }
        //生成公共支付流水信息，发起微信支付
        //发起预支付
        SaveDealRecordParam dealRecordParam = SaveDealRecordParam.builder()
                .dealType(DealTypeEnum.INCOME.getCode())
                .dealAmount(order.getOrderAmount().multiply(new BigDecimal(100)).intValue())
                .orderId(order.getId())
                .orderType(DealOrderTypeEnum.ORDER.getCode())
                .paymentType(PaymentTypeEnum.WECHAT.getCode())
                .wxTradeType(TradeTypeEnum.JSAPI)
                .wxAppTypeCode(WxAppTypeEnum.MA.getCode())
                .dealDescription(CommonConstant.DEAL_DESCRIPTION_REGISTRATION)
                .build();
        //未抛异常证明流程正常
        DealRecordVo dealRecordVo = dealRecordService.saveDealRecord(dealRecordParam);
        log.info("===========订单支付信息为 为:{}===========", dealRecordVo.getWechatPayInfo());
        order.setPayInfo(dealRecordVo.getId().toString());
        order.setPaymentUser(securityUtil.getCurrUser().getId());
        ordersMapper.updateById(order);
        //存在支付流水还要查看支付流水的状态
        ordersVo = ordersMapper.getOrderById(orderId);
        ordersVo.setWechatPayInfo(dealRecordVo.getWechatPayInfo());
        return ordersVo;
    }

    @Override
    public List<OrdersReportVo> report(OrderReportParam param) {
        //判断当前用户是否是平台管理原，不是踢出
        if (ObjectUtil.isNull(securityUtil.getCurrUser().getCurrentRole()) ||
                !(RoleEnum.PLATFORM_ADMIN.getValue().equals(securityUtil.getCurrUser().getCurrentRole().getRoleName())
                        || securityUtil.getCurrUser().getCurrentRole().getRoleName().endsWith(RoleEnum.WAREHOUSE_ADMIN.getValue()))){
            throw new XbootException("您无权执行此操作");
        }
        Long warehouseId = null;
        if (securityUtil.getCurrUser().getCurrentRole().getRoleName().endsWith(RoleEnum.WAREHOUSE_ADMIN.getValue())){
            warehouseId = securityUtil.getCurrUser().getCurrentRole().getWarehouseId();
        }
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startTime;
        String groupBy;
        DateTimeFormatter displayFormatter;

        // Set time range and format
        switch (param.getTimeSpan()) {
            case "day":
                startTime = now.minusDays(7);
                groupBy = "day";
                displayFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                break;
            case "month":
                startTime = now.minusMonths(1);
                groupBy = "day";
                displayFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                break;
            case "year":
                startTime = now.minusYears(1);
                groupBy = "month";
                displayFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
                break;
            default:
                throw new IllegalArgumentException("Invalid time span: " + param.getTimeSpan());
        }

        // Get data from database
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String startTimeStr = startTime.format(formatter);
        String endTimeStr = now.format(formatter);
        List<OrdersReportVo> dbData = ordersMapper.getOrderStatistics(
                OrderStatusEnum.PendingPayment.getValue(),
                startTimeStr,
                endTimeStr,
                groupBy,
                warehouseId
        );

        // Convert DB data to map for easy lookup
        Map<String, OrdersReportVo> dataMap = dbData.stream()
                .collect(Collectors.toMap(OrdersReportVo::getOrderDate, vo -> vo));

        // Generate complete date range
        List<OrdersReportVo> completeData = new ArrayList<>();
        LocalDateTime current = startTime;

        while (!current.isAfter(now)) {
            String dateKey = current.format(displayFormatter);
            OrdersReportVo vo = dataMap.getOrDefault(dateKey, createEmptyReport(dateKey));
            completeData.add(vo);

            // Increment date based on groupBy
            if (groupBy.equals("day")) {
                current = current.plusDays(1);
            } else {
                current = current.plusMonths(1);
            }
        }

        return completeData;
    }

    private OrdersReportVo createEmptyReport(String orderDate) {
        OrdersReportVo vo = new OrdersReportVo();
        vo.setOrderDate(orderDate);
        vo.setOrderCount(0);
        vo.setTransactionAmount(BigDecimal.ZERO);
        return vo;
    }

    @Override
    public List<PayableReportVo> getPayableReport(String reportDate) {
        if (ObjectUtil.isNull(securityUtil.getCurrUser().getCurrentRole()) ||
                !(RoleEnum.PLATFORM_ADMIN.getValue().equals(securityUtil.getCurrUser().getCurrentRole().getRoleName())
                        || securityUtil.getCurrUser().getCurrentRole().getRoleName().endsWith(RoleEnum.WAREHOUSE_ADMIN.getValue()))){
            throw new XbootException("您无权执行此操作");
        }
        Long warehouseId = null;
        if (securityUtil.getCurrUser().getCurrentRole().getRoleName().endsWith(RoleEnum.WAREHOUSE_ADMIN.getValue())){
            warehouseId = securityUtil.getCurrUser().getCurrentRole().getWarehouseId();
        }
        if (reportDate == null) {
            reportDate = DateUtils.format(LocalDate.now(),null);
        }
        return ordersMapper.getPayableReport(reportDate,warehouseId);
    }

    @Override
    public List<ReceivableReportVo> getReceivableReport(String reportDate) {
        if (ObjectUtil.isNull(securityUtil.getCurrUser().getCurrentRole()) ||
                !(RoleEnum.PLATFORM_ADMIN.getValue().equals(securityUtil.getCurrUser().getCurrentRole().getRoleName()))){
            throw new XbootException("您无权执行此操作");
        }
        if (StrUtil.isBlank(reportDate)) {
            reportDate = DateUtils.format(LocalDate.now(),null);
        }
        return ordersMapper.getReceivableReport(reportDate);
    }

    @Override
    public List<IncomeReportVo> getIncomeReport(String reportDate) {
        if (ObjectUtil.isNull(securityUtil.getCurrUser().getCurrentRole()) ||
                !(RoleEnum.PLATFORM_ADMIN.getValue().equals(securityUtil.getCurrUser().getCurrentRole().getRoleName()))){
            throw new XbootException("您无权执行此操作");
        }
        if (StrUtil.isBlank(reportDate)) {
            reportDate = DateUtils.format(LocalDate.now(), null);
        }
        return  ordersMapper.getIncomeReport(reportDate);
    }

}